package com.atguigu.gulimail.product.web;

import com.atguigu.gulimail.product.entity.CategoryEntity;
import com.atguigu.gulimail.product.service.CategoryService;
import com.atguigu.gulimail.product.vo.CataLog2Vo;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {

    @Autowired
    CategoryService categoryService;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    StringRedisTemplate stringRedisTemplate;


    @GetMapping(value = {"/", "/index"})
    public String index(Model model) {

        List<CategoryEntity> category1Level = categoryService.getCategory1Level();

        model.addAttribute("category", category1Level);
        return "index";
    }

    @ResponseBody //返回json数据
    @RequestMapping("/index/json/catalog.json")
    public Map<String, List<CataLog2Vo>> getJson() {

        Map<String, List<CataLog2Vo>> map = categoryService.getCataLogJson();

        return map;
    }

    @ResponseBody
    @RequestMapping("/hello")
    public String hello() {
        RLock lock = redissonClient.getLock("my-lock");
        //加锁
        lock.lock();    //阻塞式等待
        //设置锁的默认过期时间是30s
        //1、这个lock会不断给锁续期。 不存在业务逻辑时间过长。而锁消失的可能。
        //2、如果处理业务逻辑的时候。断电或者程序挂了。就不会给锁自动续期。 30s后就自动删除锁。 那么其他微服务就可以重新获取到锁了。
        // 不会有死锁问题
        //模拟业务逻辑代码
        // lock.lock(10, TimeUnit.SECONDS); // 手动设置锁过期时间。
        try {
            System.out.println("加锁成功，处理业务" + Thread.currentThread().getId());
            Thread.sleep(30000);
        } catch (Exception e) {

        } finally {
            System.out.println("释放锁" + Thread.currentThread().getId());
            //解锁
            lock.unlock();
        }
        return "hello";
    }


    /**
     * 模拟读写锁   如果只有读锁的话，有跟没有是一样的
     * 如果写锁跟读锁同时存在，那么必须等到读锁完成之后，写锁才能执行
     * 写锁是互斥的。 意思就是在并发的情况下 只能有一个写锁能拿到锁，其他的都得排队去拿写锁。
     * 读锁是共享的，就是所以的并发都能拿到读锁。
     * 写锁拿到锁，然后执行业务逻辑，读锁必须等待写锁执行完毕之后，才能读取数据。
     */

    @GetMapping("/write")
    @ResponseBody
    public String write() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();

        // rLock.lock(100, TimeUnit.SECONDS);

        String s = null;
        try {
            rLock.lock();       //这里必须上锁。 如果不上锁的话。没有已使意义。
            System.out.println("写锁加锁成功：" + Thread.currentThread().getId());
            s = UUID.randomUUID().toString();
            Thread.sleep(30000);

            stringRedisTemplate.opsForValue().set("rwValue", s);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            System.out.println("写锁释放：" + Thread.currentThread().getId());
        }
        return s;
    }

    @GetMapping("/read")
    @ResponseBody
    public String read() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.readLock();

        String rwValu = null;
        try {
            rLock.lock();
            System.out.println("读锁加锁成功：" + Thread.currentThread().getId());
            rwValu = stringRedisTemplate.opsForValue().get("rwValue");
        } finally {
            System.out.println("读锁释放：" + Thread.currentThread().getId());
            rLock.unlock();
        }
        return rwValu;
    }

    /**
     * 在juc中 ：
     * 比如有10个任务。  只有当10个线程都把任务做完了，那才算全部完成。
     * 分布式闭锁：
     * 多个微服务都完成了之后， 才会执行
     */
    //举一个关门的例子  5个班的人都走了。才关大门
    @ResponseBody
    @GetMapping("/closeDoor")
    public String closeDoor() throws InterruptedException {
        RCountDownLatch closeDoor = redissonClient.getCountDownLatch("closeDoor");
        closeDoor.trySetCount(5);   // 这里的意思就是 每走一个班的人 数量减1   当变成0了。 就唤醒 执行
        closeDoor.await();

        return "人都走了 放假关大门";
    }

    @ResponseBody
    @GetMapping("/gogogo/{id}")
    public String gogogo(@PathVariable("id") Long id) throws InterruptedException {
        RCountDownLatch closeDoor = redissonClient.getCountDownLatch("closeDoor");
        closeDoor.countDown(); //没执行一次  减少一个

        return id + "班：人都走了";
    }

    //信号量。
    //模拟停车   首先在redis
    @GetMapping("/park")
    @ResponseBody
    public String park() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        //park.acquire();     //这是一个阻塞的方法。 如果 reids 里面park值=0那么他就一直等待。
        boolean b = park.tryAcquire();//这是一个异步非阻塞的方法。 有空位我就停 没有我就走
        if (b) {
            return "停车成功" + b;
        } else {
            return "不停了" + b;
        }

    }

    @GetMapping("/go")
    @ResponseBody
    public String go() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        park.release();     //释放一个信号，也就是 车开走了 留一个车位

        return "车离开了 空一个车位";
    }

}
